#include "box.h"
#include "codeports.h"
#include <stdlib.h>
#include <string.h>
#include <magic.h>
#include <wand/MagickWand.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <bzlib.h>

void
BOX_Allocate(BOX *box, int width, int height)
{
	box->info.width = width;
	box->info.height = height;
	SCR_GetDepth(&box->info.bpp);
	box->buffer = malloc(box->info.width * box->info.height * box->info.bpp);
}

void
BOX_Free(BOX *box)
{
	free(box->buffer);
}

void
BOX_Clear(BOX *box)
{
	memset(box->buffer, 0, box->info.width * box->info.height * box->info.bpp);
	/*
	unsigned int i, j;
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			switch(box->info.bpp)
			{
				case 1:
				{
					BOX_SetPixelColor(box, i, j, 0);
					break;
				}
				case 2:
				{
					BOX_SetPixelRGB16(box, i, j, 0, 0, 0);
					break;
				}
				case 3:
				{
					BOX_SetPixelRGB24(box, i, j, 0, 0, 0);
					break;
				}
				case 4:
				{
					BOX_SetPixelRGBA(box, i, j, 0, 0, 0, 0);
					break;
				}
			}
		}
	}
	*/
}

void
BOX_SetPixelColor(BOX *box, int x, int y, unsigned char c)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
		box->buffer[(y * box->info.width + x) * box->info.bpp] = c;
}

void
BOX_SetPixelRGB8(BOX *box, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
		box->buffer[y * box->info.width + x] = (r >> 6) << 6 | (g >> 5) << 3 | b >> 5;
}

void
BOX_SetPixelRGB16(BOX *box, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
		((unsigned short *) box->buffer)[y * box->info.width + x] = (r >> 3) << 11 | (g  >> 2) << 5 | b >> 3;
}

void
BOX_SetPixelRGB24(BOX *box, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		box->buffer[(y * box->info.width + x) * box->info.bpp + 0] = r;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 1] = g;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 2] = b;
	}
}

void
BOX_SetPixelRGB32(BOX *box, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		box->buffer[(y * box->info.width + x) * box->info.bpp + 0] = r;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 1] = g;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 2] = b;
	}
}

void
BOX_SetPixelRGBA(BOX *box, int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		box->buffer[(y * box->info.width + x) * box->info.bpp + 0] = r;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 1] = g;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 2] = b;
		box->buffer[(y * box->info.width + x) * box->info.bpp + 3] = a;
	}
}

void
BOX_GetPixelColor(BOX *box, int x, int y, unsigned char *c)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
		*c = box->buffer[(y * box->info.width + x) * box->info.bpp];
}

void
BOX_GetPixelRGB8(BOX *box, int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		unsigned char p = box->buffer[y * box->info.width + x];
		*r = ((p & 0xC0) >> 6) << 6;
		*g = ((p & 0x38) >> 3) << 5;
		*b = (p & 0x07) << 5;
	}
}

void
BOX_GetPixelRGB16(BOX *box, int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		unsigned short p = ((unsigned short *) box->buffer)[y * box->info.width + x];
		*r = ((p & 0xF800) >> 11) << 3;
		*g = ((p & 0x07E0) >> 5) << 2;
		*b = (p & 0x001F) << 3;
	}
}

void
BOX_GetPixelRGB24(BOX *box, int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		*r = box->buffer[(y * box->info.width + x) * box->info.bpp + 0];
		*g = box->buffer[(y * box->info.width + x) * box->info.bpp + 1];
		*b = box->buffer[(y * box->info.width + x) * box->info.bpp + 2];
	}
}

void
BOX_GetPixelRGB32(BOX *box, int x, int y, unsigned char *r, unsigned char *g, unsigned char *b)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		*r = box->buffer[(y * box->info.width + x) * box->info.bpp + 0];
		*g = box->buffer[(y * box->info.width + x) * box->info.bpp + 1];
		*b = box->buffer[(y * box->info.width + x) * box->info.bpp + 2];
	}
}

void
BOX_GetPixelRGBA(BOX *box, int x, int y, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
{
	if(0 <= x && x < box->info.width && 0 <= y && y < box->info.height)
	{
		*r = box->buffer[(y * box->info.width + x) * box->info.bpp + 0];
		*g = box->buffer[(y * box->info.width + x) * box->info.bpp + 1];
		*b = box->buffer[(y * box->info.width + x) * box->info.bpp + 2];
		*a = box->buffer[(y * box->info.width + x) * box->info.bpp + 3];
	}
}

void
BOX_SetImage(BOX *box, char *image)
{
	MagickWand *mw;
	char *buffer;
	unsigned int i, j;
	mw = NewMagickWand();
	MagickReadImage(mw, image);
	if(box->info.width != MagickGetImageWidth(mw) || box->info.height != MagickGetImageHeight(mw))
		MagickScaleImage(mw, box->info.width, box->info.height);
	if(box->info.bpp != MagickGetImageDepth(mw))
		MagickSetImageDepth(mw, box->info.bpp);
	buffer = malloc(box->info.width * box->info.height * 4);
	MagickExportImagePixels(mw, 0, 0, box->info.width, box->info.height, "RGBO", CharPixel, buffer);
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			switch(box->info.bpp)
			{
				case 1:
				{
					BOX_SetPixelRGB8(box, i, j, buffer[(j * box->info.width + i) * 4 + 0], buffer[(j * box->info.width + i) * 4 + 1] , buffer[(j * box->info.width + i) * 4 + 2]);
					break;
				}
				case 2:
				{
					BOX_SetPixelRGB16(box, i, j, buffer[(j * box->info.width + i) * 4 + 0], buffer[(j * box->info.width + i) * 4 + 1] , buffer[(j * box->info.width + i) * 4 + 2]);
					break;
				}
				case 3:
				{
					BOX_SetPixelRGB24(box, i, j, buffer[(j * box->info.width + i) * 4 + 0], buffer[(j * box->info.width + i) * 4 + 1] , buffer[(j * box->info.width + i) * 4 + 2]);
					break;
				}
				case 4:
				{
					BOX_SetPixelRGBA(box, i, j, buffer[(j * box->info.width + i) * 4 + 0], buffer[(j * box->info.width + i) * 4 + 1] , buffer[(j * box->info.width + i) * 4 + 2], buffer[(j * box->info.width + i) * 4 + 3]);
					break;
				}
			}
		}
	}
	free(buffer);
	DestroyMagickWand(mw);
}

void
BOX_GetImage(BOX *box, char *image)
{
	MagickWand *mw;
	unsigned char *buffer;
	unsigned int i, j;
	unsigned char r, g, b, a;
	buffer = malloc(box->info.width * box->info.height * 4);
	for(j = 0; j < box->info.height; j++)
	{
		for(i = 0; i < box->info.width; i++)
		{
			switch(box->info.bpp)
			{
				case 1:
				{
					BOX_GetPixelRGB8(box, i, j, &r, &g, &b);
					buffer[(j * box->info.width + i) * 4 + 0] = r;
					buffer[(j * box->info.width + i) * 4 + 1] = g;
					buffer[(j * box->info.width + i) * 4 + 2] = b;
					buffer[(j * box->info.width + i) * 4 + 3] = 0;
					break;
				}
				case 2:
				{
					BOX_GetPixelRGB16(box, i, j, &r, &g, &b);
					buffer[(j * box->info.width + i) * 4 + 0] = r;
					buffer[(j * box->info.width + i) * 4 + 1] = g;
					buffer[(j * box->info.width + i) * 4 + 2] = b;
					buffer[(j * box->info.width + i) * 4 + 3] = 0;
					break;
				}
				case 3:
				{
					BOX_GetPixelRGB24(box, i, j, &r, &g, &b);
					buffer[(j * box->info.width + i) * 4 + 0] = r;
					buffer[(j * box->info.width + i) * 4 + 1] = g;
					buffer[(j * box->info.width + i) * 4 + 2] = b;
					buffer[(j * box->info.width + i) * 4 + 3] = 0;
					break;
				}
				case 4:
				{
					BOX_GetPixelRGBA(box, i, j, &r, &g, &b, &a);
					buffer[(j * box->info.width + i) * 4 + 0] = r;
					buffer[(j * box->info.width + i) * 4 + 1] = g;
					buffer[(j * box->info.width + i) * 4 + 2] = b;
					buffer[(j * box->info.width + i) * 4 + 3] = a;
					break;
				}
			}
		}
	}
	mw = NewMagickWand();
	MagickConstituteImage(mw, box->info.width, box->info.height, "RGBO", CharPixel, buffer);
	free(buffer);
	MagickWriteImage(mw, image);
	DestroyMagickWand(mw);
}
